#!/usr/bin/perl

use strict;
use warnings; 
use DBI;

# Constants
my $FORMAT = "%f\t%s\t%s\t%f\n";
my $TIMEOUT = 3; # seconds
my $TOVAL = "NaN";
my $DBFILE = "ping64.db";
my $TSHARK_CMD = "tshark -i en0 -p -l -f 'icmp or icmp6 or ip proto 41' -R 'icmp || icmpv6' -t e";

# Open the capture stream
open(TSHARK, "$TSHARK_CMD|");

# Open the database file
my $db = DBI->connect("DBI:SQLite:$DBFILE", "", "");
my $insert_query = "INSERT INTO ping64 (time, src, dst, rtt) VALUES (?,?,?,?);";
my $iq = $db->prepare($insert_query);

# Cache requests by string SRC|DST|ID|SEQ
my %cache = ();

my $line;
while ($line = <TSHARK>) {
    # Only process Echo packets
    if ($line !~ /Echo/) { next; }

    # Extract time, src, dst, type, id, seq
    chomp $line;
    my @tokens = split /\s+/, $line;
    # $tokens[0] == ""
    my $time = $tokens[0];
    my $src = $tokens[1];
    # $tokens[2] == "->"
    my $dst = $tokens[3];
    # $tokens[(4,5,6)] == "ICMPv6 Echo (ping)"
    my $type = $tokens[7];
    my $id = $tokens[8];
    my $seq = $tokens[9];

    $id =~ s/^.*=//;
    $id =~ s/,$//;
    $seq =~ s/^.*=//;
    $seq =~ s/,$//;

    # Construct an identifier for the transaction
    # ... and process the request / reply
    my $key;
    if ($type eq "request") {
        $key = "$src|$dst|$id|$seq";
        $cache{$key} = $time;
    } else {
        $key = "$dst|$src|$id|$seq";
        if ($cache{$key}) {
            my $rtt = $time - $cache{$key};
            # printf $FORMAT, $cache{$key}, $dst, $src, $rtt;
            $iq->execute($time, $dst, $src, $rtt);
        } else {
            # Do nothing; spurious reply
        }
    }

    # Cull old keys from the cache
    foreach my $key (keys %cache) {
        if ($cache{$key} - $time > $TIMEOUT) {
            my ($tosrc, $todst, $toid, $toseq) = split(/|/,$key);
            # printf $FORMAT, $cache{$key}, $tosrc, $todst, $TOVAL;
            $iq->execute($time, $dst, $src, -1);
            delete $cache{$key};
        }
    }
}



